MODULE SEDSerp;
IMPORT SEDBase, Random,  SEDE, Out:=DebugLog;

CONST POS = TRUE;
CONST NEG = FALSE;

TYPE PT= SEDBase.PT;
TYPE Ray=SEDBase.Ray;
TYPE Voxel = SEDBase.Voxel;
TYPE COLOR = SEDBase.COLOR;

VAR
	blox*: ARRAY 3,3,3 OF BOOLEAN;
	r,g,b,airred, airgreen, airblue, airblack: REAL;
	rand: Random.Generator;
	
PROCEDURE Init;
VAR
	i,j,k: INTEGER;
BEGIN
	NEW(rand);
	r:=1;
	g:=1;
	b:=1;
	airred:= 0.01;
	airgreen:=0.01;
	airblue:=0.01;
	airblack:=0.01;
	FOR i := 0 TO 2  DO FOR j := 0 TO 2 DO FOR k := 0 TO 2 DO 
		blox[i,j,k]:=TRUE
	END END END;
	blox[1,1,0]:=FALSE;
	blox[1,0,1]:=FALSE;
	blox[0,1,1]:=FALSE;
	blox[1,1,1]:=FALSE;
	blox[1,1,2]:=FALSE;
	blox[1,2,1]:=FALSE;
	blox[2,1,1]:=FALSE;
END Init;
	
PROCEDURE bounds(i, j, k: LONGINT; VAR out: BOOLEAN);
BEGIN
	IF (i < 0) OR (i >2 ) OR (j < 0) OR (j > 2) OR (k < 0) OR (k > 2) THEN
		out := TRUE
	ELSE
		out := FALSE
	END
END bounds;

PROCEDURE Shade*(VAR ray: Ray);
VAR
	oldxyz, newxyz: SEDBase.PT;
	ijk: SEDBase.IPT;
	dot, drx, dry, drz, dr, vdepth,rr,gr,br,bl: REAL;
	iter, di, dj, dk: INTEGER;
	out, shadenil,done,A,B,C: BOOLEAN;
	v: BOOLEAN;
	lx, ly, lz, distance: REAL;
BEGIN	
	INC(ray.recursion);
	ray.scale := ray.scale*3;
	oldxyz := ray.xyz;
	ray.xyz.x := ray.lxyz.x * 3  - ray.dxyz.x / 1000000 ;
	ray.xyz.y := ray.lxyz.y * 3  - ray.dxyz.y / 1000000 ;		
	ray.xyz.z := ray.lxyz.z * 3  - ray.dxyz.z / 1000000 ; 	
	SEDE.E(ray.xyz,ijk);
	bounds(ijk.i,ijk.j,ijk.k, out);
	IF ~out THEN
		v := blox[ijk.i,ijk.j,ijk.k];
		IF  v  THEN
			IF ray.recursion>3 THEN
				(* we are inside a solid cube*)
				ray.r:=1; ray.g:=1; ray.b:=1;
				ray.terminate := TRUE;	
			ELSE			
				ray.lxyz.x := ABS(ray.xyz.x - ijk.i);
				ray.lxyz.y := ABS(ray.xyz.y - ijk.j);
				ray.lxyz.z := ABS(ray.xyz.z - ijk.k);
				Shade(ray)
			END;
		END
	ELSE
		ray.terminate:=TRUE
	END;
	IF ~ray.terminate THEN 
		REPEAT
			IF di > 0 THEN
				drx := ( (ijk.i + 1) - ray.xyz.x) / ray.dxyz.x
			ELSE
				drx :=  (ijk.i -  ray.xyz.x) / ray.dxyz.x
			END;
			IF dj > 0 THEN
				dry := ( (ijk.j + 1) - ray.xyz.y) / ray.dxyz.y
			ELSE
				dry :=  (ijk.j - ray.xyz.y) / ray.dxyz.y
			END;
			IF dk > 0 THEN
				drz := ( (ijk.k + 1) - ray.xyz.z) / ray.dxyz.z
			ELSE
				drz :=  (ijk.k - ray.xyz.z) / ray.dxyz.z
			END;
			A:=drx<dry; B:=drx<drz; C:=dry<drz;	
			IF A&B THEN
				dr := drx;
				INC(ijk.i, di);
				newxyz.x := ray.xyz.x + drx * ray.dxyz.x; newxyz.y := ray.xyz.y + drx * ray.dxyz.y; newxyz.z  := ray.xyz.z + drx * ray.dxyz.z	
			ELSIF A&~B THEN
				dr := drz;
				INC(ijk.k, dk);
				newxyz.x := ray.xyz.x + drz * ray.dxyz.x; newxyz.y := ray.xyz.y + drz * ray.dxyz.y; newxyz.z  := ray.xyz.z + drz * ray.dxyz.z
			ELSIF C THEN
				dr := dry;
				INC(ijk.j, dj);
				newxyz.x := ray.xyz.x + dry * ray.dxyz.x; newxyz.y := ray.xyz.y + dry * ray.dxyz.y; newxyz.z  := ray.xyz.z+ dry * ray.dxyz.z
			ELSE
				dr := drz;
				INC(ijk.k, dk);
				newxyz.x := ray.xyz.x + drz * ray.dxyz.x; newxyz.y := ray.xyz.y + drz * ray.dxyz.y; newxyz.z  := ray.xyz.z + drz * ray.dxyz.z
			END;					
			vdepth:=SEDBase.distance(newxyz,ray.xyz);
			ray.length:=ray.length+vdepth/ray.scale;
			ray.xyz:=newxyz;
			rr := ray.ra*airred*vdepth; gr := ray.ga*airgreen*vdepth; br := ray.ba*airblue*vdepth; bl:=airblack*vdepth;
 			ray.r := ray.r + rr;
			ray.g:= ray.g + gr;
			ray.b := ray.b + br;
			ray.ra := ray.ra -rr - bl;
			ray.ga := ray.ga -gr -bl;
			ray.ba := ray.ba -br -bl;
			bounds(ijk.i,ijk.j,ijk.k, out);
			IF ~out THEN
				v := blox[ijk.i,ijk.j,ijk.k];
				IF v THEN 
					(* ray has hit a solid cube *)
					ray.lxyz.x := ABS(ray.xyz.x - ijk.i);
					ray.lxyz.y := ABS(ray.xyz.y - ijk.j);
					ray.lxyz.z := ABS(ray.xyz.z - ijk.k);
					IF ray.recursion<4 THEN
						(* expand the cube following the fractal rule *)
						Shade(ray)
					ELSE
						(*inlined shader for "solid":the ray has hit ie do not continue down into the fractal*)
						dot := ABS(ray.normal.x*ray.dxyz.x + ray.normal.y*ray.dxyz.y+ ray.normal.z*ray.dxyz.z);
						ray.r := ray.r + ((r*dot)+0.2)*ray.ra;
						ray.g := ray.g + ((g*dot)+0.2)*ray.ga;
						ray.b := ray.b + ((b*dot)+0.2)*ray.ba;		
						ray.terminate := TRUE
					END;
				END
			ELSE
				ray.terminate:=TRUE
			END;			
	UNTIL TRUE OR ray.terminate;		
	END;
	ray.scale := ray.scale/3;
	ray.xyz := oldxyz;
	DEC(ray.recursion)
END Shade;

BEGIN
	Init;	
END SEDSerp.




